﻿using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
using Xms.Abp.Core;

namespace Xms.Abp.Caching;

/// <summary>
/// https://blog.csdn.net/weixin_47082274/article/details/106723697
/// https://blog.csdn.net/minicto/article/details/78779402
/// </summary>
public class CacheDistributedSemaphoreProvider : IDistributedSemaphoreProvider, ISingletonDependency
{
    public string Name => "Cache";
    protected IAbpLazyServiceProvider LazyServiceProvider { get; }
    private IXmsCache _cache;
    protected IXmsCache Cache
    {
        get
        {
            if (_cache == null)
            {
                var options = LazyServiceProvider.LazyGetRequiredService<IOptions<RedisCacheOptions>>().Value;
                _cache = RedisCacheProvider.CreateCache(LazyServiceProvider, options.Configuration);
            }
            return _cache;
        }
    }

    public CacheDistributedSemaphoreProvider(IAbpLazyServiceProvider lazyServiceProvider)
    {
        LazyServiceProvider = lazyServiceProvider;
    }
    private static string GetKey(string semaphoreName)
    {
        return $"{{Semaphore}}:{semaphoreName}";
    }

    public bool Release(string semaphoreName, int releaseCount = 1)
    {
        return AsyncHelper.RunSync(async () => await ReleaseAsync(semaphoreName, releaseCount));
    }

    public async Task<bool> ReleaseAsync(string semaphoreName, int releaseCount = 1, CancellationToken cancellationToken = default)
    {
        return await Cache.LockReleaseAsync(GetKey(semaphoreName), releaseCount, cancellationToken);
    }

    public bool CheckSemaphore(string semaphoreName, TimeSpan timeout, int initialCount = 1)
    {
        return AsyncHelper.RunSync(async () => await CheckSemaphoreAsync(semaphoreName, timeout, initialCount));
    }

    public async Task<bool> CheckSemaphoreAsync(string semaphoreName, TimeSpan timeout, int initialCount = 1, CancellationToken cancellationToken = default)
    {
        return await Cache.LockTaskAsync(GetKey(semaphoreName), initialCount, timeout, cancellationToken);
    }

    public void Dispose(string semaphoreName)
    {
        Cache.Remove(GetKey(semaphoreName));
    }
}
